package com.bvgol.examples.my.thread.completableFutureTest;

import com.alibaba.fastjson.JSONObject;
import com.bvgol.examples.my.thread.completableFutureDemo.UserInfo;
import lombok.extern.slf4j.Slf4j;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.*;

/**
 * @program: my-spring-completely
 * @description: 全方位示例CompletableFuture常规使用
 * @author: GUOCHEN
 * @create: 2023/03/21 10:36
 */
@Slf4j
public class CompletableFutureTT {
    ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, 6L, TimeUnit.SECONDS, new LinkedBlockingQueue<>());

    public void closedThreadPool() {
        if (!executor.isShutdown()) {
            executor.shutdownNow();
        }
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFutureTT t = new CompletableFutureTT();
        t.ordinary("2023年3月21日11:06:11");
        log.info("----------------------------------------------------");
        log.info("----------------------------------------------------");
        log.info("----------------------------------------------------");
        log.info("----------------------------------------------------");
        t.completableFuture("2023年3月21日13:43:22");
        log.info("----------------------------------------------------");
        log.info("----------------------------------------------------");
        log.info("----------------------------------------------------");
        log.info("----------------------------------------------------");
        t.completableFutureAll("2023年3月21日16:40:44");
        log.info("----------------------------------------------------");
        log.info("----------------------------------------------------");
        log.info("----------------------------------------------------");
        log.info("----------------------------------------------------");
//        t.completableFutureallofone("2023年3月21日16:48:23");

        t.closedThreadPool();

    }

    /**
     * 模拟业务耗时方法
     *
     * @param name
     * @return
     * @throws InterruptedException
     */
    public String getName(String name) throws InterruptedException {
        log.info("进入getName 方法-----------------" + Thread.currentThread().getName());
        TimeUnit.SECONDS.sleep(3);
        log.info("离开getName 方法-----------------" + Thread.currentThread().getName());
        return name;
    }

    /**
     * 模拟业务耗时方法
     *
     * @param name
     * @return
     * @throws InterruptedException
     */
    public UserInfo getUserInfo(String name) throws InterruptedException {
        log.info("进入 getUserInfo 方法-----------------" + Thread.currentThread().getName());
        TimeUnit.SECONDS.sleep(3);
        log.info("离开 getUserInfo 方法-----------------" + Thread.currentThread().getName());
        return UserInfo.builder().name(name).build();
    }

    /**
     * 模拟业务耗时方法
     *
     * @param name
     * @return
     * @throws InterruptedException
     */
    public Integer getJobId(String name) throws InterruptedException {
        log.info("进入 getJobId 方法-----------------" + Thread.currentThread().getName());
        TimeUnit.SECONDS.sleep(3);
        log.info("离开 getJobId 方法-----------------" + Thread.currentThread().getName());
        return name.length();
    }

    /**
     * 模拟业务异常
     *
     * @param name
     * @return
     * @throws InterruptedException
     */
    public UserInfo getException(String name) throws InterruptedException {
        log.info("进入 getException 方法-----------------" + Thread.currentThread().getName());
        TimeUnit.SECONDS.sleep(3);
        log.info("离开 getException 方法-----------------" + Thread.currentThread().getName());
//        int i = 1 / 0;
        return null;
    }

    /**
     * 模拟业务封装总方法串行(普通)调用
     *
     * @param name
     * @return
     * @throws InterruptedException
     */
    public JSONObject ordinary(String name) {
        log.info("普通调用开始------");
        LocalDateTime start = LocalDateTime.now();

        JSONObject result = new JSONObject();
        Integer jobId = null;
        try {
            jobId = this.getJobId(name);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        String name1 = "";
        try {
            name1 = this.getName(name);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        UserInfo userInfo = null;
        try {
            userInfo = this.getUserInfo(name);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        UserInfo exception = null;
        try {
            exception = this.getException(name);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        result.put("getJobId", jobId);
        result.put("getName", name1);
        result.put("getUserInfo", JSONObject.toJSONString(userInfo));
        result.put("getException", JSONObject.toJSONString(exception));
        log.info(result.toJSONString());
        LocalDateTime end = LocalDateTime.now();
        log.info("普通调用结束------耗时:" + ChronoUnit.SECONDS.between(start, end));
        return result;
    }

    /**
     * completableFuture A+B
     *
     * @param name
     * @return
     * @throws ExecutionException
     * @throws InterruptedException
     */
    public JSONObject completableFuture(String name) throws ExecutionException, InterruptedException {
        log.info("completableFuture调用开始------" + Thread.currentThread().getName());
        LocalDateTime start = LocalDateTime.now();
        JSONObject result = new JSONObject();

        CompletableFuture<JSONObject> infoFuture = CompletableFuture.supplyAsync(() -> {
            Integer jobId = null;
            try {
                jobId = this.getJobId(name);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            result.put("getJobId", jobId);
            return result;
        }, executor);
        //res 就是上一步返回的参数不一样
        //<Void> 意思是这一步没有返回值,这一步和上一步
        CompletableFuture<Void> getName = infoFuture.thenAcceptAsync((res) -> {
            String name1 = null;
            try {
                name1 = this.getName(name);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            res.put("getName", name1);
        }, executor);
        CompletableFuture<Void> getUserInfo = infoFuture.thenAcceptAsync((res) -> {
            UserInfo userInfo = null;
            try {
                userInfo = this.getUserInfo(name);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            res.put("getUserInfo", JSONObject.toJSONString(userInfo));
        }, executor);

        CompletableFuture<Void> getException = infoFuture.thenAcceptAsync((res) -> {
            UserInfo exception = null;
            try {
                exception = this.getException(name);
            } catch (InterruptedException e) {
                log.error("我这里有个错误{}", "getException");
            }
            res.put("getException", JSONObject.toJSONString(exception));
        }, executor);

        //等到所有任务都完成
        CompletableFuture.allOf(infoFuture, getName, getUserInfo, getException).get();
        log.info(result.toJSONString());
        log.info("completableFuture调用结束------耗时:" + ChronoUnit.SECONDS.between(start, LocalDateTime.now()));
        return result;
    }

    /**
     * completableFuture 最大值/同时并发
     */
    public JSONObject completableFutureAll(String name) throws ExecutionException, InterruptedException {
        log.info("completableFutureAll 调用开始------" + Thread.currentThread().getName());
        LocalDateTime start = LocalDateTime.now();
        JSONObject result = new JSONObject();

        CompletableFuture<Void> getJobId = CompletableFuture.runAsync(() -> {
            Integer jobId = null;
            try {
                jobId = this.getJobId(name);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            result.put("getJobId", jobId);
        }, executor);
        CompletableFuture<Void> getName = getJobId.thenRunAsync(() -> {
            String name1 = null;
            try {
                name1 = this.getName(name);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            result.put("getName", name1);
        }, executor);
        CompletableFuture<Void> getUserInfo = getJobId.thenRunAsync(() -> {
            UserInfo userInfo = null;
            try {
                userInfo = this.getUserInfo(name);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            result.put("getUserInfo", JSONObject.toJSONString(userInfo));
        }, executor);
        CompletableFuture<Void> getException = getJobId.thenRunAsync(() -> {
            UserInfo userInfo = null;
            try {
                userInfo = this.getException(name);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            result.put("getUserInfo", JSONObject.toJSONString(userInfo));
        }, executor);

        //等到所有任务都完成
        CompletableFuture.allOf(getJobId, getName, getUserInfo, getException).get();
        log.info(result.toJSONString());
        log.info("completableFutureAll 调用结束------耗时:" + ChronoUnit.SECONDS.between(start, LocalDateTime.now()));
        return result;
    }

    /**
     * completableFuture 只等待主future 其它不等待
     */
    public JSONObject completableFutureallofone(String name) throws ExecutionException, InterruptedException {
        log.info("completableFutureAll 调用开始------" + Thread.currentThread().getName());
        LocalDateTime start = LocalDateTime.now();
        JSONObject result = new JSONObject();

        CompletableFuture<Void> getJobId = CompletableFuture.runAsync(() -> {
            Integer jobId = null;
            try {
                jobId = this.getJobId(name);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            result.put("getJobId", jobId);
        }, executor);
         getJobId.thenRunAsync(() -> {
            String name1 = null;
            try {
                name1 = this.getName(name);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            result.put("getName", name1);
        }, executor);
         getJobId.thenRunAsync(() -> {
            UserInfo userInfo = null;
            try {
                userInfo = this.getUserInfo(name);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            result.put("getUserInfo", JSONObject.toJSONString(userInfo));
        }, executor);
         getJobId.thenRunAsync(() -> {
            UserInfo userInfo = null;
            try {
                userInfo = this.getException(name);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            result.put("getUserInfo", JSONObject.toJSONString(userInfo));
        }, executor);

        //等到所有任务都完成
        //少了3个future 又因为模拟业务代码中有睡眠所以会报错,java.lang.InterruptedException: sleep interrupted
        CompletableFuture.allOf(getJobId).get();

        log.info(result.toJSONString());
        log.info("completableFutureAll 调用结束------耗时:" + ChronoUnit.SECONDS.between(start, LocalDateTime.now()));
        return result;
    }

}
